Skip to content

Merge upstream AnkiDroid (2.26.0alpha1) - #58

Merged
float3 merged 44 commits into
ankiquestfrom
merge/upstream-sep30
Oct 4, 2026
Merged

float3 merged 44 commits into
ankiquestfrom
merge/upstream-sep30

Conversation

@float3

@float3 float3 commented Sep 30, 2026

Copy link
Copy Markdown
Owner

42 upstream commits (2.26.0alpha1), resolving #57's conflict:

  • AnkiDroid/build.gradle: upstream's versionCode=22600101; the AnkiQuest release/nightly block moves below testInstrumentationRunner, away from the lines upstream bumps every release, so the next version bump merges without a conflict
  • still one versionCode literal for upstream's validateVersionCode
  • note: upstream now lints with checkDependencies = true

Merge with a merge commit; #57 then shows as merged.

david-allison and others added 30 commits September 29, 2026 16:21
Part of 19572

Assisted-by: GPT-6
This enables `UnusedResources` and `StringFormatMatches` to run
 cross-module, which will be necessary when our strings move to
 `:common:android`

Part of 21500

Assisted-by: Claude Fable 5
`sdkmanager` can fail with "Error on ZipFile unknown archive" before the
emulator starts.

Pre-install the emulator to work around this while upstream fixes the bug

Fixes 21885
Related to https://redirect.github.com/ReactiveCircus/android-emulator-runner/issues/496

Assisted-by: GPT-6
This allows searching when tags are loading, or with an empty collection.

Part of 10397

Assisted-by: GPT-6
In preparation for better handling of progress, errors and overlap protection.

Part of 10397

Assisted-by: GPT-6
Stops an operation from clearing progress while another is running.

This now ignores requests made while an operation is in progress
 (the UI should block operations when this occurs).

* Remember the unfinished job
* Check completion via 'isCompleted'
* set the instance variable for the next job before running it
* State is only modified for accepted requests

Part of 10397

Assisted-by: GPT-6
e.g. a tag rename

Part of 10397

Assisted-by: GPT-6
A screen rotation could hide a snackbar before a user had time to see it

Part of 10397

Assisted-by: GPT-6
Gradle task validation failed when lint and jacocoUnitTestReport ran in
one invocation because report tasks scanned all of build/ for coverage.
RuntimeEnvironment.setQualifiers(+night) recreates ActivityThread leftovers
(often DeckPicker), which flakes ThemesTest mid-suite. Add an after-test
guard and dispose the current offenders.

tearDown force-removes owned controllers only; clearAll would hide
ActivityScenario leftovers from NoLiveRobolectricActivitiesRule.

Preserve original test failures alongside leak reports via
MultipleFailureException, and use a low Rule.order so the check runs
outside other rules after their activity cleanup finishes.

Dispose AppCompat delegates during leak cleanup, and report owned
activity cleanup failures after remaining teardown finishes.

Assisted-by: GPT-6
Co-authored-by: Cursor <cursoragent@cursor.com>
checkDependencies rejects setting WrongThread to informational while :common leaves that Android check disabled. Informational enables it without failing :common:lint.

Co-authored-by: Cursor <cursoragent@cursor.com>
For a locale-only tag such as {{tts fr_FR:Front}}, voiceForTag fell
back on the first voice matching the language without checking
availability. Android can list voices flagged KEY_FEATURE_NOT_INSTALLED,
so an uninstalled voice could be chosen and fail on playback with
ERROR_NOT_INSTALLED_YET even when an installed voice for that language
was available.

The language fallback now prefers an installed voice, only settling for
an unavailable one when nothing else matches.

The fix and its tests come from David Allison's review suggestion,
generated with GPT-6.

Co-authored-by: David Allison <62114487+david-allison@users.noreply.github.com>
Assisted-by: GPT-6 [fix and tests]
An Activity can report `STARTED` after a `finish()` call: if a WebView
renderer callback executed, if this opens a dialog, it crashes.

In these exceptional cases, skip displaying the dialog.

Fixes 6244

Assisted-by: GPT-6
* Manage Fields
* Deck Picker + Study Options

Part of 21794

Assisted-by: GPT-6
FAB is still in the background

Part of 21794

Assisted-by: GPT-6
Content was covered when entering custom study options

Part of 21794

Assisted-by: GPT-6
WebView text-only zoom (`textZoom`) does not scale em-based layouts (used by MathJax).

It would have required fragile, font-specific workarounds to fix on the MathJax level:
* `text-size-adjust` had no effect.
* `font-size-adjust` required compensation for each font.

Instead, set `textZoom` to 100 (from 130 [system-dependent]) and scale using CSS zoom.
This scales the text size and layout together.

BREAKING CHANGE:
Some layouts are changed by this, see my list on 20776

Fixes 20776

Assisted-by: GPT-6
Part of 20776

Assisted-by: GPT-6
Keeps a strong reference to a pending snackbar so it can't be GCed

The reference is removed when the snackbar is shown or dismissed
 (without clearing a newer replacement).

Fixes 22076

Assisted-by: GPT-6
`AppLifecycleObserver.onStop` blocked on the collection queue,
This blocked WorkManager's timeout callback, crashing the app if
`dataSync` timed out.

```
android.app.RemoteServiceException$ForegroundServiceDidNotStopInTimeException: A foreground service of type dataSync did not stop within its timeout: ComponentInfo{com.ichi2.anki/androidx.work.impl.foreground.SystemForegroundService}
	at android.app.ActivityThread.generateForegroundServiceDidNotStopInTimeException(ActivityThread.java:2875)
	at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2837)
	at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException(Unknown Source:0)
```

Fixes 19749

Assisted-by: GPT-6
With the keyboard up in landscape, Filter by tag froze AnkiDroid for
seconds at a time and could end in "AnkiDroid isn't responding". The
dialog had so little height left that the tag list was laid out with a
negative height, which RecyclerView read as a huge one, so it built a
row for every tag, again on each keystroke.

The list now sits in its own container, so it shrinks to nothing
instead.

Part of 22082

Assisted-by: Opus 5.5
On a phone in landscape, opening the keyboard squeezed the tags dialog
into the strip above it: the toolbar was cut off and no tags were
visible.

On short screens the dialog now keeps its size and the keyboard covers
its lower part, so the search field and the first matches stay in view,
and the buttons come back once the keyboard closes. Rotating with the
dialog open switches between the two.

Fixes 22082

Assisted-by: Opus 5.5
Add a ViewModelSavedStateHandle alias and a factory that supplies an empty
set of default arguments to AndroidX. Keep the remaining creation extras
intact so saved-state ownership and restoration still use AndroidX.

Document that the alias alone does not filter arguments and previously
saved values remain intact. Launch inputs belong in constructor parameters.
Existing ViewModels are not migrated in this preparatory change.

Test activity and fragment owners with EmptyApplication, including an
unfiltered model that must still receive the original owner arguments.
Both cases fail when the factory copies those arguments.

Part of 21953

Assisted-by: GPT-6
Part of 21953

Assisted-by: GPT-6
ExportReadyViewModel copied any intent extras from AnkiActivity into its
instance state, potentially triggering a `TransactionTooLargeException`.

Use our new `savedStateViewModelFactory` to avoid this.

Part of 21953

Assisted-by: GPT-6
Calling `.cancel()` on the progress monitor did not wait for completion,
so a notification + 'Cancel', associated with a completed WorkManager
could appear.

Fix: make the monitor a child of the sync operation via `coroutineScope`. The scope will
 then wait for the monitor to finish before cleaning up notifications.

Fixes 19830

Assisted-by: GPT-6
AnkiDroid Translations and others added 13 commits September 30, 2026 09:28
Disable on-the-fly Robolectric SDK downloads.

On Windows, parallel test workers can fail to open the lock,
to resolve dependencies, failing even if the jar is already cached
locally, causing unexpected test failures.

Now, Gradle resolves the jars into its shared dependency cache before
forking tests. Each worktree writes a small properties file pointing to
those cached jars, avoiding an additional 1.45 GiB of copies per worktree.

`robolectric-deps.properties` is set so PropertiesDependencyResolver is
used, which reads the cached jars without downloading or acquiring a
Maven download lock.

Comments changed: robolectric no longer downloads into ~/.m2

Fixes 22069

java.lang.IllegalStateException: Couldn't create lock file C:\Users\runneradmin\.robolectric-download-lock

Assisted-by: GPT-6
Fix validation (and layout) for the custom toolbar item dialogs:

- disable the create button until an icon and at leas one of
  before/after text is entered
- align margins in the add/edit toolbar item layouts

Part of 20508

Co-authored-by: David Allison <62114487+david-allison@users.noreply.github.com>
Assisted-by: Claude Fable 5 (David - a11y fixes)
In 26.09, save & optimize performed a refresh, which was a legitimate bug
if there was a delayed response.

These extracted functions allow the next commit to focus on the race
condition via properly detecting a reload.

Part of 21926

Assisted-by: GPT-6
Part of 21926

Assisted-by: GPT-6
Reading a cloned response was not a good signal.
Instead, observe `blob()`.

Assisted-by: GPT-6
We want to guard against navigation while the operation is
in progress.

fetch/body-read errors are rethrown (frontend displays them).

Assisted-by: GPT-6
The 26.09 frontend reloads immediately after starting Save & Optimize.

This needs to be deferred until we have consumed the response

* if we get a failure; we don't want to navigate away,
  as this loses state
* if we navigate during the frontend reading the body,
  it is treated as an error

Requesting the reload is still required for 26.05.

Part of 21926

Assisted-by: GPT-6
* drain media callbacks
* await the `ANSWER_EASY` result

Fixes 22172

Assisted-by: GPT-6
Leaks are now associated with the tests which leak

This would have flagged `testCommandPerformsAnswerCard` earlier
 and associated the issue with the correct test.

Part of 22172

Assisted-by: GPT-6
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Snapshot diff report vs ankiquest. Open screenshot-diff for diffs.

  • AnkiquestHomeScreenshotTest: 1 change
  • DeckPickerScreenshotTest: 3 changes
  • DeckPickerTabletScreenshotTest: 1 change
  • NavigationDrawerScreenshotTest: 1 change
  • NoteTypeFieldEditorScreenshotTest: 1 change
  • ReviewRemindersScreenshotTest: 5 changes
  • StudyOptionsScreenshotTest: 1 change
All 13 changed screenshots

AnkiquestHomeScreenshotTest

  • today_320dp_compare.png

DeckPickerScreenshotTest

  • deck_list_after_dismissing_create_deck_dialog_compare.png
  • hierarchy_lines_collapsed_compare.png
  • keyboard_open_scrolled_to_bottom_compare.png

DeckPickerTabletScreenshotTest

  • keyboard_open_compare.png

NavigationDrawerScreenshotTest

  • navigationDrawer_compare.png

NoteTypeFieldEditorScreenshotTest

  • keyboard_open_compare.png

ReviewRemindersScreenshotTest

  • studyOptionsFragmentHost_scheduleReminders_compare.png
  • studyOptionsFragmentHost_systemBars_compare.png
  • studyOptionsFragmentHost_systemBars_scrolledToEnd_compare.png
  • studyOptionsFragmentHost_troubleshooting_compare.png
  • studyOptionsFragmentHost_troubleshooting_systemBars_compare.png

StudyOptionsScreenshotTest

  • keyboard_open_compare.png

Upstream's Robolectric setup now resolves SDKs offline from robolectricDownloader.gradle, which has no Android 7.0 (API 24), so AnkiquestNotifierTest failed to initialize. API 25 is in the list and also predates notification channels.
@float3
float3 merged commit 67d3ae8 into ankiquest Oct 4, 2026
27 checks passed
@float3
float3 deleted the merge/upstream-sep30 branch October 4, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants